package server

import (
	"context"
	"log"
	"net/http"
	"os"

	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"minio.com/minio-sdk/mask"
	"minio.com/minio-sdk/util"
)

type Server struct {
	logger      *log.Logger
	minioClient *minio.Client
	withMask    bool
	maskData    []byte
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	if r.URL.Path == "/get" {
		bucket := r.FormValue("bucket")
		oid := r.FormValue("oid")
		object, err := s.minioClient.GetObject(context.Background(), bucket, oid, minio.GetObjectOptions{})
		if err != nil {
			r.Body.Close()
			s.logger.Panicln("minio get obj err:", err)
		}
		defer object.Close()
		objStat, _ := object.Stat()
		objContent := make([]byte, int(objStat.Size))
		length, err := object.Read(objContent)
		if length != int(objStat.Size) {
			r.Body.Close()
			s.logger.Panicf("get object content error, objsize: %d, read len: %d, err: %s\n", objStat.Size, length, err)
		}

		// mask data
		if s.withMask {
			if err = mask.XorDataWithMask(objContent, s.maskData); err != nil {
				s.logger.Panicln("mask data chunk error:", err)
			}
			s.logger.Println("done mask data chunk:", oid)
		}

		length, err = w.Write(objContent)
		if err != nil {
			r.Body.Close()
			s.logger.Panicf("write to client error, objsize: %d, write len: %d, err: %s\n", objStat.Size, length, err)
		}
		s.logger.Println("done write file to client:", oid)
	} else {
		log.Println("[HTTP server] ->", "invalid URL:", r.URL.Path)
	}
}

func StartServer(minioConfig util.Minio_config) error {
	// set log
	logFile, err := os.OpenFile(util.LoggerFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		log.Println("open log file failed, err:", err)
		return err
	}
	defer logFile.Close()
	logger := log.New(logFile, "[HTTP server] -> ", log.Lshortfile|log.Ldate|log.Ltime)

	ctx := context.Background()

	// Initialize minio client.
	minioClient, err := minio.New(minioConfig.Endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(minioConfig.AccessKeyID, minioConfig.SecretAccessKey, ""),
		Secure: minioConfig.UseSSL,
	})
	if err != nil {
		logger.Fatalln(err)
	}

	err = minioClient.MakeBucket(ctx, minioConfig.Bucket, minio.MakeBucketOptions{})
	if err != nil {
		// Check to see if we already own this bucket (which happens if you run this twice)
		exists, errBucketExists := minioClient.BucketExists(ctx, minioConfig.Bucket)
		if errBucketExists == nil && exists {
			logger.Printf("We already own %s\n", minioConfig.Bucket)
		} else {
			logger.Fatalln(err)
		}
	} else {
		logger.Printf("Successfully created %s\n", minioConfig.Bucket)
	}

	s := Server{
		logger:      logger,
		minioClient: minioClient,
		withMask:    minioConfig.WithMasks,
		maskData:    minioConfig.MaskData,
	}
	server := &http.Server{
		Addr:    util.SERVER_PORT,
		Handler: &s,
	}
	logger.Println("begin HTTP server ... ")
	err = server.ListenAndServe()
	if err != nil {
		logger.Println("ListenAndServe err:", err)
	}
	return nil
}
